import Tone from "../core/Tone";
import "../core/Context";
import "../shim/AudioNode";

/**
 *  @class Tone.AudioNode is the base class for classes which process audio.
 *         AudioNodes have inputs and outputs.
 *  @param	{AudioContext=} context	The audio context to use with the class
 *  @extends {Tone}
 */
Tone.AudioNode = function(){
	Tone.call(this);

	//use the default context if one is not passed in
	var options = Tone.defaults(arguments, ["context"], {
		"context" : Tone.context
	});

	/**
	 * The AudioContext of this instance
	 * @private
	 * @type {AudioContext}
	 */
	this._context = options.context;
};

Tone.extend(Tone.AudioNode);

/**
 * Get the audio context belonging to this instance.
 * @type {Tone.Context}
 * @memberOf Tone.AudioNode#
 * @name context
 * @readOnly
 */
Object.defineProperty(Tone.AudioNode.prototype, "context", {
	get : function(){
		return this._context;
	}
});

/**
 *  Create input and outputs for this object.
 *  @param  {Number}  [input=0]   The number of inputs
 *  @param  {Number}  [outputs=0]  The number of outputs
 *  @return  {Tone.AudioNode}  this
 *  @private
 */
Tone.AudioNode.prototype.createInsOuts = function(inputs, outputs){

	if (inputs === 1){
		this.input = this.context.createGain();
	} else if (inputs > 1){
		this.input = new Array(inputs);
	}

	if (outputs === 1){
		this.output = this.context.createGain();
	} else if (outputs > 1){
		this.output = new Array(outputs);
	}
};

/**
 *  channelCount is the number of channels used when up-mixing and down-mixing
 *  connections to any inputs to the node. The default value is 2 except for
 *  specific nodes where its value is specially determined.
 *
 *  @memberof Tone.AudioNode#
 *  @type {Number}
 *  @name channelCount
 *  @readOnly
 */
Object.defineProperty(Tone.AudioNode.prototype, "channelCount", {
	get : function(){
		return this.output.channelCount;
	},
	set : function(c){
		return this.output.channelCount = c;
	}
});

/**
 *  channelCountMode determines how channels will be counted when up-mixing and
 *  down-mixing connections to any inputs to the node.
 *  The default value is "max". This attribute has no effect for nodes with no inputs.
 *  @memberof Tone.AudioNode#
 *  @type {String}
 *  @name channelCountMode
 *  @readOnly
 */
Object.defineProperty(Tone.AudioNode.prototype, "channelCountMode", {
	get : function(){
		return this.output.channelCountMode;
	},
	set : function(m){
		return this.output.channelCountMode = m;
	}
});

/**
 *  channelInterpretation determines how individual channels will be treated
 *  when up-mixing and down-mixing connections to any inputs to the node.
 *  The default value is "speakers".
 *  @memberof Tone.AudioNode#
 *  @type {String}
 *  @name channelInterpretation
 *  @readOnly
 */
Object.defineProperty(Tone.AudioNode.prototype, "channelInterpretation", {
	get : function(){
		return this.output.channelInterpretation;
	},
	set : function(i){
		return this.output.channelInterpretation = i;
	}
});

/**
 *  The number of inputs feeding into the AudioNode.
 *  For source nodes, this will be 0.
 *  @type {Number}
 *  @name numberOfInputs
 *  @memberof Tone.AudioNode#
 *  @readOnly
 */
Object.defineProperty(Tone.AudioNode.prototype, "numberOfInputs", {
	get : function(){
		if (this.input){
			if (Tone.isArray(this.input)){
				return this.input.length;
			} else {
				return 1;
			}
		} else {
			return 0;
		}
	}
});

/**
 *  The number of outputs coming out of the AudioNode.
 *  @type {Number}
 *  @name numberOfOutputs
 *  @memberof Tone.AudioNode#
 *  @readOnly
 */
Object.defineProperty(Tone.AudioNode.prototype, "numberOfOutputs", {
	get : function(){
		if (this.output){
			if (Tone.isArray(this.output)){
				return this.output.length;
			} else {
				return 1;
			}
		} else {
			return 0;
		}
	}
});

/**
 *  connect the output of a ToneNode to an AudioParam, AudioNode, or ToneNode
 *  @param  {Tone | AudioParam | AudioNode} unit
 *  @param {number} [outputNum=0] optionally which output to connect from
 *  @param {number} [inputNum=0] optionally which input to connect to
 *  @returns {Tone.AudioNode} this
 */
Tone.AudioNode.prototype.connect = function(unit, outputNum, inputNum){
	if (Tone.isArray(this.output)){
		outputNum = Tone.defaultArg(outputNum, 0);
		this.output[outputNum].connect(unit, 0, inputNum);
	} else {
		Tone.connect(this.output, unit, outputNum, inputNum);
	}
	return this;
};

/**
 *  disconnect the output
 *  @param {Number|AudioNode} output Either the output index to disconnect
 *                                   if the output is an array, or the
 *                                   node to disconnect from.
 *  @returns {Tone.AudioNode} this
 */
Tone.AudioNode.prototype.disconnect = function(destination, outputNum, inputNum){
	if (Tone.isArray(this.output)){
		outputNum = Tone.defaultArg(outputNum, 0);
		this.output[outputNum].disconnect(destination, 0, inputNum);
	} else {
		Tone.disconnect(this.output, destination, outputNum, inputNum);
	}
	return this;
};

/**
 *  Connect the output of this node to the rest of the nodes in series.
 *  @example
 *  //connect a node to an effect, panVol and then to the master output
 *  node.chain(effect, panVol, Tone.Master);
 *  @param {...(AudioParam|Tone|AudioNode)} nodes
 *  @returns {Tone.AudioNode} this
 */
Tone.AudioNode.prototype.chain = function(){
	var args = Array.from(arguments);
	args.unshift(this);
	Tone.connectSeries.apply(undefined, args);
	return this;
};

/**
 *  connect the output of this node to the rest of the nodes in parallel.
 *  @param {...(AudioParam|Tone|AudioNode)} nodes
 *  @returns {Tone.AudioNode} this
 */
Tone.AudioNode.prototype.fan = function(){
	for (var i = 0; i < arguments.length; i++){
		this.connect(arguments[i]);
	}
	return this;
};

/**
 * Dispose and disconnect
 * @return {Tone.AudioNode} this
 */
Tone.AudioNode.prototype.dispose = function(){
	if (Tone.isDefined(this.input)){
		if (this.input instanceof AudioNode){
			this.input.disconnect();
		}
		this.input = null;
	}
	if (Tone.isDefined(this.output)){
		if (this.output instanceof AudioNode){
			this.output.disconnect();
		}
		this.output = null;
	}
	this._context = null;
	return this;
};

export default Tone.AudioNode;

